package blackstar.plugin.performance.stat;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import blackstar.performance.logger.info.LoadChainNode;
import blackstar.performance.logger.info.LoadChainTree;

public class DetailStater
{
	private final static Log LOGGER = LogFactory.getLog(DetailStater.class);

	private Map<String, MergeLoadChainNode> stats = new HashMap<String, MergeLoadChainNode>();

	public void add(File file) throws IOException
	{
		InputStream is = new FileInputStream(file);
		try
		{
			add(is);
		} finally
		{
			is.close();
		}
	}

	private void add(InputStream inputStream) throws IOException
	{
		add(new InputStreamReader(inputStream));
	}

	private void add(Reader reader) throws IOException
	{
		LoadChainTree currentTree = null;
		BufferedReader bufferReader = new BufferedReader(reader);
		String line = null;
		String className;
		String methodName;
		int ignore = 0;
		while ((line = bufferReader.readLine()) != null)
		{
			String[] array = line.split("\\|");
			if (array.length >= 4)
			{
				className = array[1];
				methodName = array[2];
				if (currentTree == null)
				{
					currentTree = new LoadChainTree();
				}

				if ("1".equals(array[0]))
				{
					currentTree.start(className, methodName, 0L);

				} else if ("2".equals(array[0]))
				{
					currentTree.stop(Long.parseLong(array[3]));
				} else
				{
					ignore++;
					continue;
				}

				if (currentTree.isFinish())
				{
					LoadChainNode root = currentTree.getRoot();
					if (root != null)
					{
						MergeLoadChainNode mlcn = stats.get(root.getFullName());
						if (mlcn == null)
						{
							mlcn = new MergeLoadChainNode(root);
							stats.put(root.getFullName(), mlcn);
						} else
						{
							mlcn.merge(root);
						}

						mlcn = null;
						root = null;

						currentTree = null;
					}
				}
			} else
			{
				ignore++;
			}
			array = null;
		}

		LOGGER.info("handle reader[" + reader + "] success.Igore[" + ignore
				+ "]");
	}

	public void genToFile(File file) throws IOException
	{
		if (file.exists())
		{
			file.delete();
		}

		File dir = file.getParentFile();
		if (!dir.exists())
		{
			dir.mkdirs();
		}

		FileWriter fileWriter = new FileWriter(file);

		try
		{
			final BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
			bufferedWriter
					.write("Levle|ClassName|MethodName|TotalInvokes|TotalTimeNano|Percent"
							+ "\n");

			long tmpTotalTime = 0;
			for (MergeLoadChainNode item : stats.values())
			{
				tmpTotalTime += item.getTotalTime();
			}
			final NumberFormat format = NumberFormat.getInstance();
			format.setMaximumFractionDigits(3);
			final long totalTime = tmpTotalTime;
			LOGGER.info("State Size:" + stats.size() + ",StateTime:"
					+ totalTime);
			List<MergeLoadChainNode> list = new ArrayList<MergeLoadChainNode>(
					stats.values());
			Collections.sort(list, new Comparator<MergeLoadChainNode>()
			{
				public int compare(MergeLoadChainNode o1, MergeLoadChainNode o2)
				{
					return new Long(o2.getTotalTime()).compareTo(new Long(o1
							.getTotalTime()));
				}
			});
			for (MergeLoadChainNode item : list)
			{
				item.visitDepthFirst(new IMergeLoadChainNodeVisitor()
				{
					private int index = 0;

					public void visitBegin(MergeLoadChainNode node)
					{
						try
						{

							bufferedWriter.write(index + "|"
									+ node.getClassName() + "|"
									+ node.getMethod() + "|"
									+ node.getTotalCount() + "|"
									+ node.getTotalTime() + "|");
							bufferedWriter.write(format.format(((double) node
									.getTotalTime())
									/ totalTime));
							bufferedWriter.write("\n");

						} catch (IOException e)
						{
							LOGGER.info("Error write Detail Performance file",
									e);
						}
						index++;
					}

					public void visitEnd(MergeLoadChainNode node)
					{
						index--;
					}
				});

			}
			bufferedWriter.flush();
		} finally
		{
			fileWriter.close();
		}
	}
}
